﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace MPev
{
    public class MPModel
    {
        public string statement = "";
        public string type = "1";
        public bool existVerbModifier = false;
        
        public MPGroup[] NounPart = new MPGroup[4];
        public MPGroup[] VerbPart = new MPGroup[3];

        public MPRel[] NounRel = new MPRel[3];
        public MPRel[] VerbRel = new MPRel[2];

        public MPModel()
        {
            int i;
            for (i = 0; i < 4; i++) NounPart[i] = new MPGroup();
            for (i = 0; i < 3; i++) VerbPart[i] = new MPGroup();
            for (i = 0; i < 3; i++) NounRel[i] = new MPRel();
            for (i = 0; i < 2; i++) VerbRel[i] = new MPRel();

            NounPart[0].type = MPClassType.MPNoun;
            VerbPart[0].type = MPClassType.MPVerb;
        }

        public void Save(StreamWriter sw)
        {
            sw.WriteLine(statement);
            sw.WriteLine(type);
            sw.WriteLine(existVerbModifier);
            int i;
            for (i = 0; i < 4; i++) NounPart[i].Save(sw);
            for (i = 0; i < 3; i++) VerbPart[i].Save(sw);
            for (i = 0; i < 3; i++) NounRel[i].Save(sw);
            for (i = 0; i < 2; i++) VerbRel[i].Save(sw);
        }
        public void Load(StreamReader sr)
        {
            statement = sr.ReadLine();
            type = sr.ReadLine();
            existVerbModifier = Convert.ToBoolean(sr.ReadLine());
            int i;
            for (i = 0; i < 4; i++) NounPart[i].Load(sr);
            for (i = 0; i < 3; i++) VerbPart[i].Load(sr);
            for (i = 0; i < 3; i++) NounRel[i].Load(sr);
            for (i = 0; i < 2; i++) VerbRel[i].Load(sr);
        }

        public string MPPart2String4Dot(MPGroup[] part, MPRel[] rels)
        {
            StringWriter sw = new StringWriter();
            int n = part.Count();

            sw.WriteLine(part[0].toString4Dot());

            for (int i = 1; i < n; i++)
            {
                if (part[i].list.Count() == 0) break;
                sw.WriteLine(part[i].toString4Dot());

                if (part[i].type != MPClassType.MPNoun)
                {
                    // normal model type
                    foreach (MPClass c1 in part[i - 1].list)
                    {
                        // multi label
                        int j=0;

                        List<String> labels = rels[i - 1].label.Split(',').ToList();

                        while (labels.Count() < part[i].list.Count())
                        {
                            labels.Add(labels[0]);
                        }

                        foreach (MPClass c2 in part[i].list)
                        {
                            if (rels[i - 1].type == "general" || rels[i - 1].type == "aggre")
                                // reverse direction
                                sw.WriteLine(rel2String4Dot(c2.id.ToString(), c1.id.ToString(), rels[i - 1].type, labels[j]));
                            else
                                //normal direction
                                sw.WriteLine(rel2String4Dot(c1.id.ToString(), c2.id.ToString(), rels[i - 1].type, labels[j]));
                            j++;
                        }
                    }
                }
                else
                {
                    // model type 8, 8A
                    sw.WriteLine("Realize2[label = \"{Realize2\\n\\<\\<Verb\\>\\>}\"]");
                    foreach (MPClass c in part[i - 1].list)
                    {
                        sw.WriteLine(rel2String4Dot(c.id.ToString(), "Realize2", "general", ""));
                    }
                    foreach (MPClass c in part[i].list)
                    {
                        sw.WriteLine(rel2String4Dot("Realize2", c.id.ToString(), "real", ""));
                    }
                }
                                            
            }
            return sw.ToString();
        }

        string rel2String4Dot(string from, string to, string type, string label)
        {
            StringWriter sw = new StringWriter();

            //sw.Write("{0}->{1}", from, to);
            sw.Write("{0}->{1}", from, to);

            // label
            if (label != "")
            {
                string name;
                if (type == "depend" || type == "prep" || type == "tran") name = "\\<\\<" + label + "\\>\\>";
                else name = label;
                
                sw.Write("[label=\"{0}\"]", name);
            }

            switch (type)
            {
                case "aggre":
                    sw.Write("[arrowhead=ediamond]");
                    break;
                case "general":
                    sw.Write("[arrowhead=empty]");

                    break;
                case "real":
                    sw.Write("[arrowhead=empty style=dotted]");
                    break;
                case "depend":
                case "prep":
                    sw.Write("[arrowhead=vee style=dotted]");
                    break;
                case "assoc":
                case "tran":
                    sw.Write("[arrowhead=vee]");
                    break;
            }
            sw.WriteLine();

            return sw.ToString();  
        }
                    
        public string toString4Dot()
        {
            StringWriter sw = new StringWriter();

            sw.WriteLine(MPPart2String4Dot(NounPart, NounRel));
            sw.WriteLine(MPPart2String4Dot(VerbPart, VerbRel));

            sw.WriteLine("Realize[label = \"{Realize\\n\\<\\<Verb\\>\\>}\"]");
            foreach (MPClass c in NounPart[0].list)
            {
                sw.WriteLine(rel2String4Dot("Realize", c.id.ToString(), "real", ""));
            }
            foreach (MPClass c in VerbPart[0].list)
            {
                sw.WriteLine(rel2String4Dot(c.id.ToString(), "Realize", "general", ""));
            }
            
            return sw.ToString();
        }

        private string name2String(List<MPClass> mc)
        {
            string result = "";
            foreach (MPClass mpclass in mc)
            {
                if (result == "") result = mpclass.name;
                else result += ",\\n" + mpclass.name;
            }
            return result;
        }

        private string attr2String(List<MPClass> mc)
        {
            string result = "";
            foreach (MPClass mpclass in mc)
            {
                foreach (string a in mpclass.attr)
                    if (result == "") result = a;
                    else result += "," + a;
            }
            return result;
        }

        public string partConversion(string prefix, MPGroup[] part, MPRel[] rels)
        {
            StringWriter sw = new StringWriter();
            
            for (int i = 0; i < part.Count(); i++)
            {
                if (part[i].list.Count == 0) break;
                
                if (i > 0 && rels[i - 1].type == "general") break;

                if (i < part.Count()-1 && rels[i].type == "general")
                    sw.WriteLine("{0}{1}[label=\"{2}\"]", prefix, i.ToString(), name2String(part[i].list.Concat(part[i+1].list).ToList()));
                else 
                    sw.WriteLine("{0}{1}[label=\"{2}\"]", prefix, i.ToString(), name2String(part[i].list));
                
                if (part[i].type != MPClassType.Noun)
                {
                    sw.WriteLine("{0}{1}type[label=\"{2}\"]", prefix, i.ToString(), part[i].type.ToString());
                    sw.WriteLine("{0}{1}->{0}{1}type[label=\"type\"]", prefix, i.ToString());
                }
                string attr = attr2String(part[i].list);
                if (attr != "")
                {
                    sw.WriteLine("{0}{1}attr[label=\"{2}\"]", prefix, i.ToString(), attr);
                    sw.WriteLine("{0}{1}->{0}{1}attr[label=\"attribute\"]", prefix, i);
                }
                if (i > 0)
                {
                    string label = rels[i-1].type;
                    if(rels[i-1].label != "") label += ":" + rels[i-1].label;
                                        
                    if(rels[i-1].type == "aggre") // reverse
                        sw.WriteLine("{0}{2}->{0}{1}[label=\"{3}\"]", prefix, (i - 1).ToString(), i.ToString(), label);
                    else  // normal
                        sw.WriteLine("{0}{1}->{0}{2}[label=\"{3}\"]", prefix, (i - 1).ToString(), i.ToString(), label);
                }
            }

            return sw.ToString();
        }
        
        public string graphConversion()
        {
            StringWriter sw = new StringWriter();

            sw.WriteLine(partConversion("V", VerbPart, VerbRel));
            sw.WriteLine(partConversion("N", NounPart, NounRel));

            return sw.ToString();
        }

    }
}
